package util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.collections.ExtendedProperties;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
* <p>
* プロパティ情報を保持します
* </p>
* 
* @author NDSS fushimi
*/
public class CustomExtendedProperties
{
   //[ソースコード診断 目視-35]start
   //Loggerはprivate static final で宣言
   //private final org.apache.commons.logging.Log log = LogFactory.getLog( CustomExtendedProperties.class );
   private static final Log log = LogFactory.getLog( CustomExtendedProperties.class );
 //[ソースコード診断 目視-35]end
   
   // 内部で保持するプロパティ
   private ExtendedProperties _prop = new ExtendedProperties();

   // 追加ファイルのkey
   private static final String KEY_INCLUDE = "include";

   // 読み込むプロパティファイルのエンコード
   private static final String DEFAULT_FILE_ENC = "UTF-8";

   // 排他制御用フラグ
   private boolean readyToRead = true;
   //[ソースコード診断 TRS.SORF-2]start
   //同期対象Objectは final 指定する
   //Object lock = new Object();
   private final Object lock = new Object();
   //[ソースコード診断 TRS.SORF-2]end
   

   // エンコード
   String enc = DEFAULT_FILE_ENC;


   /**
    * <p>
    * 指定されたファイルを読み込み、インスタンスを生成します
    * </p>
    * 読み込むファイルのエンコードはUTF-8である必要があります
    * 
    * @param fileName
    *            読み込むプロパティファイル名
    */
   public CustomExtendedProperties( String fileName )
   {
       this( fileName, DEFAULT_FILE_ENC );
   }


   /**
    * <p>
    * 指定されたファイルを読み込み、インスタンスを生成します
    * </p>
    * 
    * @param fileName
    *            読み込むプロパティファイル名
    * @param enc
    *            読み込むファイルのエンコード
    */
   public CustomExtendedProperties( String fileName, String enc )
   {
       this();
       this.enc = enc;
       loadProperties( fileName );
   }


   /**
    * 指定されたプロパティファイルを読み込み、保持しているプロパティを更新します
    * 
    * @param fileName
    *            読み込むファイル名
    */
   private void loadProperties( String fileName )
   {
       synchronized ( lock )
       {
           // ロック中は参照しない
           while ( BooleanUtils.isFalse( readyToRead ) )
           {
               try
               {
                   lock.wait();
               }
               catch ( InterruptedException ignore )
               {
                   //[ソース診断対応:目視チェック start]
                   log.error( "ファイル読み込みエラー", ignore );
                   //[ソース診断対応:目視チェック end]
               }
           }
       }
       // プロパティの参照をロック
       readyToRead = false;
       try
       {
           _prop.clear();
           loadProperty( fileName );
           loadInclude();
       }
       catch ( Throwable ignore )
       {
           //[ソース診断対応:目視チェック start]
           log.error( "ファイル読み込みエラー", ignore );
           //[ソース診断対応:目視チェック end]
       }
       finally
       {
           synchronized ( lock )
           {
               // ロック解除・解除通知
               readyToRead = true;
               lock.notifyAll();
           }
       }

       // デバッグ表示
       if ( log.isDebugEnabled() )
       {
           log.debug( "プロパティファイル[" + fileName + "]読み込み完了．保持プロパティダンプ..." );
           _prop.display();
       }
   }


   /**
    * <p>
    * 空のインスタンスを生成します
    * </p>
    * プロパティファイルの内容は、{@link #load(String)}メソッドで読み込む必要があります。
    */
   public CustomExtendedProperties()
   {
       _prop.setInclude( StringUtils.EMPTY );
   }


   // /**
   // * 指定されたファイルをプロパティとして読み込みます
   // *
   // * @param fileName
   // * プロパティファイル名
   // */
   // public void load(String fileName) {
   // loadProperty(fileName);
   // }

   /**
    * <p>
    * 内部で保持するプロパティ情報を取得します。
    * </p>
    * 
    * @return 内部で保持する<code>ExtendedProperties</code>
    */
   public ExtendedProperties getProperties()
   {
       synchronized ( lock )
       {
           // ロック中は参照しない
           while ( BooleanUtils.isFalse( readyToRead ) )
           {
               try
               {
                   lock.wait();
               }
               catch ( InterruptedException ignore )
               {
                   //[ソース診断対応:目視チェック start]
                   log.error( "ファイル読み込みエラー", ignore );
                   //[ソース診断対応:目視チェック end]
               }
           }
       }
       return _prop;
   }


   /**
    * 追加で読み込むように指定されたプロパティを読み込みます。
    */
   private void loadInclude()
   {
       String[] fileNames = _prop.getStringArray( KEY_INCLUDE );
       for ( String s : fileNames )
       {
           if ( log.isDebugEnabled() )
           {
               log.debug( "追加ファイル読み込み[" + s + "]" );
           }
           loadProperty( s );
       }
   }


   /**
    * 指定されたファイルを、UTF-8で読み込み、保持しているプロパティに追加します。
    * 
    * @param fileName
    *            追加するプロパティファイル名
    */
   private void loadProperty( String fileName )
   {

       InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream( fileName );
       if ( is == null )
       {
           is = SystemSettingUtil.class.getClassLoader().getResourceAsStream( "/" + fileName );
       }
       
       //ファイルを読み込めた場合のみ、リソースを取得
       if(is != null){
           try
           {
               _prop.load( is, enc );
           }
           catch ( IOException e )
           {
               // 例外発生時に抜けてしまうのを防ぐ
               //[ソース診断対応:目視チェック start]
               //Exception.printStackTrace() を利用しない
               //e.printStackTrace();
               log.error( "ファイル読み込みエラー", e );
               //[ソース診断対応:目視チェック end]
           }
           finally
           {
               if ( is != null )
                   try
                   {
                       is.close();
                   }
                   catch ( IOException ignore )
                   {
                       //[ソース診断対応:目視チェック start]
                       log.error( "ファイル読み込みエラー", ignore );
                       //[ソース診断対応:目視チェック end]
                   }
           }
       }
   }


   /**
    * <p>
    * プロパティを新たに設定します
    * </p>
    * 
    * @param key
    *            追加するプロパティのキー
    * @param value
    *            追加するプロパティ値
    */
   public void addProperty( String key, Object value )
   {
       getProperties().addProperty( key, value );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>Object</code>で取得します
    * </p>
    * 
    * @param key
    *            プロパティキー
    */
   public Object getProperty( String key )
   {
       return getProperties().getProperty( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>String</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、<code>null</code>が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public String getString( String key )
   {
       return getProperties().getString( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>String</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、デフォルト値が取得されます。
    * 
    * @param key
    *            プロパティキー
    * @param defaultValue
    *            デフォルト値
    */
   public String getString( String key, String defaultValue )
   {
       return getProperties().getString( key, defaultValue );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>String</code>の<code>List</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、空の<code>List</code>が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public List<String> getStringList( String key )
   {
       return Arrays.asList( getProperties().getStringArray( key ) );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>String</code>の配列で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、空の配列が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public String[] getStringArray( String key )
   {
       return getProperties().getStringArray( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>boolean</code>で取得します
    * </p>
    * <code>boolean</code>として取得可能な値は yes/no, true/false, on/off のいずれかです。
    * 
    * @param key
    *            プロパティキー
    */
   public boolean getBoolean( String key )
   {
       return getProperties().getBoolean( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>boolean</code>で取得します
    * </p>
    * <code>boolean</code>として取得可能な値は yes/no, true/false, on/off のいずれかです。
    * 指定したキーが設定されていなかった場合は、デフォルト値が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public boolean getBoolean( String key, boolean defaultValue )
   {
       return getProperties().getBoolean( key, defaultValue );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>short</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、<code>null</code>が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public short getShort( String key )
   {
       return getProperties().getShort( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>short</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、デフォルト値が取得されます。
    * 
    * @param key
    *            プロパティキー
    * @param defaultValue
    *            デフォルト値
    */
   public short getShort( String key, short defaultValue )
   {
       return getProperties().getShort( key, defaultValue );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>int</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、<code>null</code>が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public int getInt( String key )
   {
       return getProperties().getInteger( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>int</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、デフォルト値が取得されます。
    * 
    * @param key
    *            プロパティキー
    * @param defaultValue
    *            デフォルト値
    */
   public int getInt( String key, int defaultValue )
   {
       return getProperties().getInteger( key, defaultValue );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>long</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、<code>null</code>が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public long getLong( String key )
   {
       return getProperties().getLong( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>long</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、デフォルト値が取得されます。
    * 
    * @param key
    *            プロパティキー
    * @param defaultValue
    *            デフォルト値
    */
   public long getLong( String key, long defaultValue )
   {
       return getProperties().getLong( key, defaultValue );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>double</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、<code>null</code>が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public double getDouble( String key )
   {
       return getProperties().getDouble( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>double</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、デフォルト値が取得されます。
    * 
    * @param key
    *            プロパティキー
    * @param defaultValue
    *            デフォルト値
    */
   public double getDouble( String key, double defaultValue )
   {
       return getProperties().getDouble( key, defaultValue );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>float</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、<code>null</code>が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public float getFloat( String key )
   {
       return getProperties().getFloat( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>float</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、デフォルト値が取得されます。
    * 
    * @param key
    *            プロパティキー
    * @param defaultValue
    *            デフォルト値
    */
   public double getFloat( String key, float defaultValue )
   {
       return getProperties().getFloat( key, defaultValue );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>byte</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、<code>null</code>が取得されます。
    * 
    * @param key
    *            プロパティキー
    */
   public byte getByte( String key )
   {
       return getProperties().getByte( key );
   }


   /**
    * <p>
    * 指定したキーで定義されたプロパティを<code>byte</code>で取得します
    * </p>
    * 指定したキーが設定されていなかった場合は、デフォルト値が取得されます。
    * 
    * @param key
    *            プロパティキー
    * @param defaultValue
    *            デフォルト値
    */
   public byte getByte( String key, byte defaultValue )
   {
       return getProperties().getByte( key, defaultValue );
   }

}

